﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StrategyGame
{
	/// <summary>
	/// Describes a structure which contains information about technologies that can be researched, their requirements and progress
	/// Instantiated once by each nation in the game to track that nation's progress
	/// Currently implemented as an actual tree, with each tech having one requirement and potentially many descendants
	/// 
	/// Note - This is fairly limited - ie no multiple requirements for a tech
	/// so the interface should allow for the internal structure to be changed entirely
	/// </summary>
	class TechTree
	{
		#region Public interface
		
		public delegate void ReadOnlyNodeOperation(ITechOnTree node, int tree_depth);

		public interface ITechOnTree
		{
			string TechName { get; }
			int Progress { get; }
		}

		public int GetProgressOn(string techname)
		{
			Node node = GetNodeByName(techname);
			if (node != null)
				return node.Progress;
			else
				return -1; //this means invalid tech
		}

		public bool IsResearched(string techname)
		{
			Node node = GetNodeByName(techname);
			if (node != null)
				return node.Progress >= node.ResearchCost;
			else
				return false;
		}

		public void ForEachNode(ReadOnlyNodeOperation op)
		{
			TreeBase.ForEachChild((node, depth) => op(node, depth));
		}

		#endregion

		#region Tree implementation

		class Node: ITechOnTree
		{
			public string TechName { get; private set; }
			public List<Node> Children { get; private set; }
			public Node Parent { get; private set; }

			public Node AddChild(Node new_node)
			{
				new_node.Parent = this;
				Children.Add(new_node);

				//pass through
				return new_node;
			}

			public delegate void NodeOperation(Node node, int tree_depth);

			void RecursiveForEachChild(NodeOperation op, ref int depth)
			{
				op(this, depth);

				++depth;

				foreach (Node child in Children)
					child.RecursiveForEachChild(op, ref depth);

				--depth;
			}

			public void ForEachChild(NodeOperation op)
			{
				int depth = 0;
				RecursiveForEachChild(op, ref depth);
			}
			
			public delegate bool NodeTest(Node node);

			public Node RecursiveFindNode(NodeTest test)
			{
				if (test(this))
				{
					//this node passes the test? just return it then
					return this;
				}
				else
				{
					foreach (Node child in Children)
					{
						//recurse through this node's children, performing the same check
						Node child_result = child.RecursiveFindNode(test);
						if (child_result != null)
							return child;
					}

					//a node that a) fails the test itself and b) has no children that pass the test, returns null
					return null;
				}
			}

			public int ResearchCost;
			public int Progress { get; set; }

			public Node(string name, int cost)
			{
				Parent = null;
				Children = new List<Node>();

				Progress = 0;

				TechName = name;
				ResearchCost = cost;
			}
		}

		//the top node of the tree, the starting point for all research
		readonly Node TreeBase = new Node("base", 0);

		Node GetNodeByName(string techname)
		{
			return TreeBase.RecursiveFindNode(node => node.TechName == techname);
		}

		#endregion

		public TechTree()
		{
			//todo: load this from xml or something
			var farming1 = TreeBase.AddChild(new Node("Farm I", 1000));
				var farming2 = farming1.AddChild(new Node("Farm II", 2000));
			var the_wheel = TreeBase.AddChild(new Node("The Wheel", 1000));
				var mining1 = the_wheel.AddChild(new Node("Mining I", 2000));
					var mining2 = mining1.AddChild(new Node("Mining II", 4000));
				var masonry = the_wheel.AddChild(new Node("Masonry", 2000));
					var house1 = masonry.AddChild(new Node("Housing I", 4000));
				var bronze_tools = the_wheel.AddChild(new Node("Bronze Tools", 2000));
		}
	}
}
